package task

import (
	"fmt"
	"os"

	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
	"file_explorer/service"
)

type BatchMover struct {
	BasicTask

	srcFileInfos []*model.FileInfo
	dstDir       string

	mergeFileInfos []*model.FileInfo
	copyFileInfos  []*model.FileInfo

	moveItems []*moveItem
	copyItems []*copyItem
	copySize  int64

	walker *PathWalker
	copier *fileCopier
}

type moveItem struct {
	info    *model.FileInfo
	dstPath string
}

func (task *BatchMover) GetLabel() string {
	name := task.srcFileInfos[0].Name
	if len(task.srcFileInfos) > 1 {
		name += fmt.Sprintf("等%d项", len(task.srcFileInfos))
	}
	return fmt.Sprintf("移动%v到%v", name, task.dstDir)
}

func (task *BatchMover) Pause() {
	if task.walker != nil {
		task.walker.Pause()
	}
	if task.copier != nil {
		task.copier.Pause()
	}
}

func (task *BatchMover) Cancel() {
	task.BasicTask.Cancel()
	if task.walker != nil {
		task.walker.Cancel()
	}
	if task.copier != nil {
		task.copier.Cancel()
	}
}

func (task *BatchMover) start() {
	task.BasicTask.start()
	defer task.BasicTask.finish()

	task.preProcess()
	task.scan()
	task.batchMove()
}

func (task *BatchMover) preProcess() {
	log.Debug("batch mover pre process start")

	if len(task.srcFileInfos) == 0 {
		task.fail(fmt.Errorf("未选中任何文件"))
		return
	}

	for _, srcFileInfo := range task.srcFileInfos {
		if srcFileInfo.IsDir() && (srcFileInfo.GetPath() == task.dstDir || srcFileInfo.IsParent(task.dstDir)) {
			task.fail(fmt.Errorf("父目录不可移动到子目录下"))
			return
		}

		// 位置不变
		if srcFileInfo.Dir == task.dstDir {
			continue
		}

		// 相同分区
		if util.IsSamePartition(srcFileInfo.GetPath(), task.dstDir) {
			// dst目录下存在同名文件
			if util.PathExists(util.PathJoin(task.dstDir, srcFileInfo.Name)) {
				task.mergeFileInfos = append(task.mergeFileInfos, srcFileInfo)
				continue
			}
			task.moveItems = append(task.moveItems, &moveItem{
				info:    srcFileInfo,
				dstPath: util.PathJoin(task.dstDir, srcFileInfo.Name),
			})
			continue
		}

		// 不同分区
		task.copyFileInfos = append(task.copyFileInfos, srcFileInfo)
	}

	log.Debug("batch mover pre process end")
}

func (task *BatchMover) scan() {
	log.Debug("batch mover scan start")

	// 扫描合并文件
	task.walker = NewPathWalker(task.mergeFileInfos)
	task.walker.SetOnAccess(func(root string, info *model.FileInfo) {
		task.progress = fmt.Sprintf("扫描中: %v", info.GetPath())

		srcPath := info.GetPath()
		relativePath := srcPath[len(root):]
		_, rootName := util.PathSplit(root)

		task.moveItems = append(task.moveItems, &moveItem{
			info:    info,
			dstPath: util.PathJoin(task.dstDir, rootName, relativePath),
		})
	})
	task.walker.SetOnFinish(func(_ int64, _ bool, err error) {
		task.fail(err)
	})
	task.walker.start()

	// 扫描复制文件
	task.walker = NewPathWalker(task.copyFileInfos)
	task.walker.SetOnAccess(func(root string, info *model.FileInfo) {
		task.progress = fmt.Sprintf("扫描中: %v", info.GetPath())

		srcPath := info.GetPath()
		relativePath := srcPath[len(root):]
		_, rootName := util.PathSplit(root)

		task.copyItems = append(task.copyItems, &copyItem{
			info:    info,
			dstPath: util.PathJoin(task.dstDir, rootName, relativePath),
		})

		if !info.IsDir() {
			task.copySize += info.Size
		}
	})
	task.walker.SetOnFinish(func(_ int64, _ bool, err error) {
		task.fail(err)
	})
	task.walker.start()

	log.Debug("batch mover scan end")
}

func (task *BatchMover) batchMove() {
	log.Debug("batch mover move start")

	// 同分区移动
	for i, item := range task.moveItems {
		if !task.isRunning() {
			return
		}

		// 源文件或目录不存在
		if !util.PathExists(item.info.GetPath()) {
			continue
		}

		// 目标路径下已存在
		if util.PathExists(item.dstPath) {
			// 目录冲突不处理，合并子文件
			if item.info.IsDir() {
				continue
			}
			// 文件冲突时重命名
			var err error
			item.dstPath, err = util.ResolveFilenameConflict(item.dstPath)
			if err != nil {
				task.fail(err)
				return
			}
		}

		// 移动文件或目录
		err := os.Rename(item.info.GetPath(), item.dstPath)
		if err != nil {
			task.fail(err)
			return
		}

		task.percentage = float64(i+1) / float64(len(task.moveItems))
		task.progress = fmt.Sprintf("%.2f%%, %s/%s, 同分区移动中", task.percentage*100, i+1, len(task.moveItems))
	}

	for _, item := range task.moveItems {
		if !task.isRunning() {
			return
		}

		if util.PathExists(item.info.GetPath()) {
			service.DeleteFile([]*model.FileInfo{item.info})
		}
	}

	// 跨分区复制
	copiedFileSize := int64(0)
	for _, item := range task.copyItems {
		if !task.isRunning() {
			return
		}

		if item.info.IsDir() {
			err := util.MkdirIfNotExist(item.dstPath)
			if err != nil {
				task.fail(err)
				return
			}
			continue
		}

		task.copier = newFileCopier(item.info, item.dstPath, func(curFileCopiedSize int64) {
			copiedSize := copiedFileSize + curFileCopiedSize
			task.percentage = float64(copiedSize) / float64(task.copySize)
			task.progress = fmt.Sprintf("%.2f%%, %s/%s, 跨分区移动%v中",
				task.percentage*100, util.ConvSize(copiedSize), util.ConvSize(task.copySize), item.info.Name)
		})
		task.copier.SetOnFinish(func(_ int64, _ bool, err error) {
			if !item.info.IsDir() {
				copiedFileSize += item.info.Size
			}
			task.fail(err)
		})
		task.copier.start()
	}

	for _, item := range task.copyItems {
		if !task.isRunning() {
			return
		}

		if util.PathExists(item.info.GetPath()) {
			service.DeleteFile([]*model.FileInfo{item.info})
		}
	}

	log.Debug("batch mover move end")
}

func NewBatchMover(srcFileInfos []*model.FileInfo, dstDir string) *BatchMover {
	return &BatchMover{
		BasicTask:    newBasicTask("batch mover"),
		srcFileInfos: srcFileInfos,
		dstDir:       dstDir,
	}
}
